#!/usr/bin/python
   # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU General Public License for more details.

    # You should have received a copy of the GNU General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Copyright 2012 Marcel Verpaalen
# This program reads the serial output from electronic energy meter P1 port and displays / uploads the values
 
# You need pySerial
import serial
from datetime import datetime, date, time, timedelta
import time
#import pvoutput 
import cosm
GM = ''
GT = '' 

logfile = '/home/pi/p1/gaslog.txt'
API_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
FEED = '6xxxxx'
COSM_message_var = { #this contains the datapoint ID's to use in COSM
'1-0:1.8.1' :  'm1',
'1-0:1.8.2' :  'm2',
'1-0:2.8.1' :  'm3',
'1-0:2.8.2' :  'm4',
'1-0:1.7.0' :  'P',
'1-0:2.7.0' :  'S',
'0-1:24.3.0' : 'GM'
}

P1_message_var = {
'0-0:96.1.1' : 'Serial nr Meter',
'1-0:1.8.1' :  'Levering Energie Normaal tarief',
'1-0:1.8.2' :  'Levering Energie Laag tarief',
'1-0:2.8.1' :  'TerugLevering Energie Normaal tarief',
'1-0:2.8.2' :  'TerugLevering Energie Laag tarief',
'0-0:96.14.0': 'Huidig Tarief (laag/normaal)',
'1-0:1.7.0' :  'Momenteel opgenomen vermogen',
'1-0:2.7.0' :  'Momenteel teruggeleverd vermogen',
'0-0:17.0.0' : 'Maximum stroom per fase',
'0-0:96.3.10': 'Stand stroomschakelaar ',
'0-0:96.13.1': 'Bericht numeriek ',
'0-0:96.13.0': 'Bericht Tekst ',
'0-1:24.1.0' : 'Serienummer gasmeter',
'0-1:96.1.0' : 'Andere meter aanwezig op M-bus: gasmeter ',
'0-1:24.3.0' : 'Gasmeting',
'0-1:24.4.0' : 'Stand Gasmeter klep'
}

class P1_Msg:
	def __init__(self, id='', description='',value='',unit ='',other =''):
		self.id = id
		self.description = description
		self.value = value
		self.unit = unit
		self.other = other
		
		
def read_message (ser):
#Reads complete message from P1 bus. Start with / end with !
	message=[]
	message_start = False
	message_end = False
	while not(message_end):
#		line = str(ser.readline(), "utf-8").strip()  #for python 3.x
		line = str(ser.readline()).strip()
#		print (line)
		if "/" in line: 
			if message_start :
				message_start = False
				message_end = True
				print ("Warning, incomplete message received")
			else: message_start = True
		if "!" in line:  
			message_start = False
			message_end = True
		if message_start :  message.append (line)
	return message  

def writeGas (readtime,metervalue):
				try:
					f = open(logfile, 'w')
					f.write (readtime)
					f.write ('\n')
					f.write (metervalue)					
				except: # Exception as ex:
					print ('Failed writing to log')
				finally:
					f.close()
				return
	
def splitline(line):
	pos = line.find("(")
	para = line[0:line.find("(")]
	val = line[pos+1:]
	val = val[:val.rfind(")")]
	return(para,val)
	
def hex2acii (txt):
	txt =  txt.strip()
	return ''.join([chr(int(''.join(c), 16)) for c in zip(txt[0::2],txt[1::2])])

def time2ascii	(i): #converts timestring to readable date time
	return  '20'+i[:2]+'-'+i[2:4]+'-'+i[4:6]+' '+i[6:8]+':'+i[8:10]+':'+i[10:12]

		
def process_message (message):
	i = 0
	print ("processing message")
	message_output = []
	while  i <  len(message) :
		(var,para) = splitline (message[i])
		if len(message[i]) > 1 : 
			l = P1_Msg('Unknown',message[i])
			if message[i][0]  == '/' : l = P1_Msg('ID','ID',message[i])
			if var in P1_message_var: 
#				print(P1_message_var[var],var,para)
				l = P1_Msg(var)
			#	l.id = var
				l.description = P1_message_var[var]
				l.value = para
				if l.id == '0-1:24.3.0': #gasmeting
					l.other =  time2ascii(para[:para.find(')')])
					l.unit = para[para.rfind('(')+1:]
					i = i + 1
					(var,para) = splitline (message[i])
					l.value = para
				if  ':96.1' in l.id : l.value = hex2acii (l.value) #convert hex meterID to ascii
				pos = l.value.find('*')
				if pos > 0:
					l.unit = l.value[pos+1:]
					l.value = l.value[:pos]
			message_output.append(l)	
		i = i + 1
		
	return message_output
	
def cosm_update (m):
  global GM
  global GT
  pfu = cosm.PachubeFeedUpdate(FEED,API_KEY)
  for l in m: #cycle through the message
		if  l.id in COSM_message_var : #upload value if in the COSM table
			cosm_ID = COSM_message_var[l.id]
			cosm_value = l.value
			cosm_value = cosm_value.lstrip('0')
#			if cosm_ID in ('P', 'S'): cosm_value = '{0:.0f}'.format( float(cosm_value) * 1000)  #display power with no decimals in W
			if cosm_ID in ('P', 'S'): cosm_value = int(float(cosm_value) * 1000)  #display power with no decimals in W
			pfu.addDatapoint(cosm_ID , cosm_value)
			if cosm_ID == 'GM' and GT == '':     #dirty hack for displaying the consumption of gas
				try:
					f = open(logfile, 'r')
					GT = f.readline() 
					#print (s)
					GM = f.readline() 
					#print (s)					
				except: # Exception as ex:
				  	print ('Failed reading from to log')
				finally:
				    f.close()
			if cosm_ID == 'GM' and GT == '':     #dirty hack for displaying the consumption of gas
				GM = cosm_value
				GT = l.other
				writeGas (l.other,cosm_value)
			if cosm_ID == 'GM' and l.other <> GT:     #dirty hack for displaying the consumption of gas
				m = float( float(cosm_value) - float(GM))
				nt = datetime.strptime(l.other, "%Y-%m-%d %H:%M:%S")
				ot = datetime.strptime(GT[0:19], "%Y-%m-%d %H:%M:%S")
				td = nt - ot
				h = td.days * 24 + td.seconds / 3600
				h = 1
				pfu.addDatapoint('G' , '{0:.3f}'.format( float( m / h)))

#			pfu.addDatapoint('G' , '{0:.3f}'.format( float( float(cosm_value) - float(GM))))
				GM = cosm_value
				GT = l.other
				writeGas (l.other,cosm_value)
  # finish up and submit the data
  pfu.buildUpdate()
  pfu.sendUpdate()
  return

#Main program
comport ='/dev/ttyUSB0'
comport ='/dev/ttyFTDI0'
ser = serial.Serial(comport)
ser.baudrate = 9600
ser.timeout=10
ser.xonxoff = True # . Enable software flow control.
ser.rtscts = True # . Enable hardware (RTS/CTS) flow control.
ser.dsrdtr = True #. Enable hardware (DSR/DTR) flow control
ser.flushInput() #    Flush input buffer, discarding all it.s contents.
ser.flushOutput()

# Must given Arduino time to rest.
# Any time less than this does not seem to work... (<1.5s)
time.sleep(3)

cycle = 0
while 1:
#GM = '519.847'
#GT= '2012-11-05 00:00:00'
	
	P1_message = read_message (ser) #reads all lines of single message
	m = process_message (P1_message) #processes the lines and splits and adds description
	#print (len(P1_message))
	for l in m: #cycle through and print
		if cycle == 0: print (l.id,l.description,l.value,l.unit,l.other)
	cosm_update (m)	#send data to COSM
	cycle = cycle + 1
	if cycle > 30 : cycle = 0
